#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <map>
#include <string>
using std::map;
using std::string;
volatile int sigFlag;

void SignalHandler(int sig)
{
	printf("SignalHandler, sig:%d, pid: %d\n", sig, getpid());
	sigFlag = 1;
}

void SetSignal()
{
	struct sigaction sa;
	memset(&sa, 0, sizeof(sa));
	//void (*sa_handler)(int), 信号处理函数
	//sigset_t sa_mask, 用来设置信号屏蔽集，当信号函数返回后，还原
	//int sa_flags, 指定对信号处理的各个选项
	//void (*sa_signation)(int, siginfo_t *, void *), 是一个替代的信号处理程序，当sa_flag为SA_SIGINFO时，使用该处理程序。比sa_handler带有更多的参数
	sa.sa_handler = SignalHandler;
	
	//sigaction函数
	//1. 第一个参数，捕捉的信号
	//2. 第二个参数，相应信息
	//3. 第三个参数, 如果为非空，会返回该信号的上一个动作
	//设置主进程如果被中断了，如何处理函数
	//
	//SIGINT: 用户按中断键(一般采用Delete或者Ctril+C)，终端驱动程序产生此信号，发送给前台进程组中的每个进程
	sigaction(SIGINT, &sa, NULL);
	//SIGHUP: 与终端链接断开
	sigaction(SIGHUP, &sa, NULL);
	//SIGTERM:由kill(1)命令发送的系统默认终止信号
	sigaction(SIGTERM, &sa, NULL);
	//SIGQUIT: 当用户在终端按退出键(一般采用Ctrl+\),此信号不止终止钱袋进程组，同时产生一个core文件
	sigaction(SIGQUIT, &sa, NULL);

	//signal(SIGPIPE, SIG_IGN)  
    //当服务器close一个连接时，若client端接着发数据。
    //根据TCP 协议的规定，会收到一个RST响应，client再往这个服务器发送数据时，系统会发出一个SIGPIPE信号给进程，告诉进程这个连接已经断开了，不要再写了。 
    //根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。
    //若不想客户端退出可以把SIGPIPE设为SIG_IGN 
    signal(SIGPIPE,SIG_IGN);

	//取消以下信号的屏蔽
    sigset_t sset;
    sigemptyset(&sset);
    sigaddset(&sset, SIGBUS);
    sigaddset(&sset, SIGILL);
    sigaddset(&sset, SIGFPE);
    sigaddset(&sset, SIGSEGV);
    sigaddset(&sset, SIGCHLD);//设置child中断时产生的信号不会被阻塞
    sigaddset(&sset, SIGABRT);
    //设置信号屏蔽
    //1. 第一个参数， SIG_BLOCK是或操作，表示新增需要被屏蔽的信号
    //SIG_UNBLOCK是取消以下信号的阻塞
    //SIG_SETMASK是赋值操作，设置需要屏蔽的信号，并与原先无关
    //2. 第二个参数，需要修改的信号
    //3. 第三个参数，如果不是空，获得操作后的信号屏蔽信息
    sigprocmask(SIG_UNBLOCK, &sset, &sset);
}

void init()
{
	sigFlag = 0;
	//设置信号
	SetSignal();

	//设置守护进程
	//1.第一个参数:为0时,将根目录修改为工作目录 
	//2.第二个参数:为0时,做输入,输出以及错误输出重定向到/dev/null 
	daemon(1, 1);
}

pid_t ForkChild(int channel)
{
	pid_t p = fork();
	if(p < 0) {
		return -1;
	}

	if(p != 0){ //parent
		return p;
	}

	printf("creat child[%d]\n", channel);
	//child
	while(!sigFlag){
		printf("child[%d]\n", channel);
		sleep(3);
	}
	printf("-----------child[%d]_end------------\n", channel);
	//必须是exit不能是return，否则会继续进行
	exit(10);
}

int main()
{
	//初始化
	init();

	map<pid_t, int> list;
	map<pid_t, int>::iterator iter;
	//生成两个子进程
	for(int i = 0; i < 2; i++){
		pid_t p = ForkChild(i);
		list[p] = i;
	}

	while(!sigFlag){
		int status = 0;
		//watipid函数用来等待父进程接收到子进程的SIGCHLD信号
		//1. 第一个参数，pid_t pid
		//pid == -1: 等待任一子进程，此种情况下与wait函数相等
		//pid > 0: 等待进程ID与pid相等的子进程
		//pid == 0:等待组ID等于调用进程组ID的任一子进程
		//pid < -1:等待组ID等于pid绝对值的任一子进程
		//2. 第二个参数，int *statloc,存放终止进程的终止状态
		//3. 第三个参数 int option, 进一步控制waitpid操作，可支持作业控制，以及设置wait的非阻塞版本，<<Uinix环境高级编程>>P193
		//4. 成功返回进程id，失败返回0
		printf("--------waitpid----------\n");
		pid_t p = waitpid(-1, &status, 0);//此处waitpid相当于wait函数
		if(p == 0) {
			continue;
		}
		printf("--------pid:%d----------\n", p);
		iter = list.find(p);
		if(iter == list.end()){
			printf("GET error pid!");
			break;
		}
		//重新拉起子进程
		int channel = iter->second;
		list.erase(iter);
		p = ForkChild(channel);
		list[p] = channel;
	}

	printf("-----sigFlag:%d-------------\n",sigFlag);

	//父进程被kill之后，杀死两个子进程
	for(iter = list.begin(); iter != list.end(); iter++) {
		if(iter->first != -1){
			kill(iter->first, SIGTERM);
		}
	}

	return 0;
}
